﻿package
{
	import org.flixel.*;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.events.SampleDataEvent;
	import flash.display.LoaderInfo;
	import flash.media.SoundMixer;
	import flash.media.SoundTransform;
	import flash.external.ExternalInterface;
	import org.flixel.data.FlxFlash;
	import flash.utils.Endian;
	
	public class PlayState extends FlxState
	{
		//Create an instance of the Sound class
		private var soundClip:Sound;
			//Create a new SoundChannel Object
		private var sndChannel:SoundChannel;
		
		private var loadCount:int;
		private var sounds:Array;
		private var soundChannels:Array;
		
		private var refresh:Boolean = false;
		
		
		private var BUFFER_SIZE:int = 7350;
		
		private var buffer:Vector.<ByteArray> = new Vector.<ByteArray>(4);
		private var normal:Vector.<Boolean> = new Vector.<Boolean>(4);
		private var sound:Vector.<Sound> = new Vector.<Sound>(4);
		private var lengths:Vector.<int> = new Vector.<int>(4);
		private var volumes:Vector.<Number> = new Vector.<Number>(4);
		private var layers:Vector.<Array> = new Vector.<Array>(4);

		private var sndCChannel:SoundChannel;
		//private var mastervolume:Number;
	
		
		private function nearestPowerOfTwo(i:int):int
		{

			   var x:int = ((i - 1) & i);

			   return x ? nearestPowerOfTwo(x) : i << 1;

			
		}   
		
		
		private const MAX_RATIO:Number = 1 / uint.MAX_VALUE;

		private var r:uint = Math.random() * uint.MAX_VALUE;
		 
		// returns a number from 0 - 1
		private function XORandom():Number{
			r ^= (r << 21);
			r ^= (r >>> 35);
			r ^= (r << 4);
			return (r * MAX_RATIO);
		}
		
		
		private var loopCount:int = 0;
		private var measureLength:int;
		
		private var fadeOut:Boolean = false;
		
		private var position:int = 0;
		
		private var tempbuff:ByteArray;
		private var interLoopCount:int = 0;
		
		private var seed:int;
		private var granularity:int;
		private var currentPlaying:Number;
		
		private function onSampleData(sampleDataEvent:SampleDataEvent) : void
		{
			if (int(sampleDataEvent.position / BUFFER_SIZE) % measureLength == 0)
			{
				if (int(sampleDataEvent.position / BUFFER_SIZE) != 0 )
				{
					if (++loopCount > 31)
					{
						loopCount = 0;
						
						if (refresh)
						{
							fadeOut = true;
							if(radio)
								ExternalInterface.call("radioNext");
							else
								ExternalInterface.call("reloadPage");
						}
					}
					interLoopCount = 0;
				}
			}
			
			var current:int;
			
			
			

			currentPlaying = 1;
			
			
		  for (var i:int = 0; i <  4; i++) 
		  {
			buffer[i].position = 0;
			
			switch(layers[i][loopCount])
			{
				
				case 0:
					currentPlaying+=.1;
				default:
					if((interLoopCount==0 && loopCount%4==0 && !normal[i]) || int(sampleDataEvent.position/BUFFER_SIZE)%lengths[i]==0)
						sound[i].extract(buffer[i], BUFFER_SIZE,0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = true;
				break;
				case 1:
					if ((interLoopCount%granularity) % seed == 0)
						sound[i].extract(buffer[i], BUFFER_SIZE, 0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
				case 2:
					if ((interLoopCount%granularity) - seed == 0)
						sound[i].extract(buffer[i], BUFFER_SIZE, 0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
				case 3:
					if ((interLoopCount%granularity) < seed)
						sound[i].extract(buffer[i], BUFFER_SIZE, 0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
				case 7:
					if ((interLoopCount%(granularity)) % int(XORandom()*seed + 1) == 0)
						sound[i].extract(buffer[i], BUFFER_SIZE, 0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
				case 8:
					if((interLoopCount==0 && loopCount%4==0 && !normal[i]) || int(sampleDataEvent.position/BUFFER_SIZE)%lengths[i]==0)
						sound[i].extract(buffer[i], BUFFER_SIZE,0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = true;
				break;
				case 9:
					if ((interLoopCount%granularity) % seed == 0)
						sound[i].extract(buffer[i], BUFFER_SIZE, (loopCount%4) * BUFFER_SIZE*8);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
				case 10:
					sound[i].extract(buffer[i], BUFFER_SIZE, (31 - interLoopCount) * BUFFER_SIZE);
					normal[i] = false;
				break;
				case 11:
					sound[i].extract(buffer[i], BUFFER_SIZE, int(XORandom() * 16) * BUFFER_SIZE);
					normal[i] = false;
				break;
				case 12:
					if ((interLoopCount%(granularity/i)) == 0)
						sound[i].extract(buffer[i], BUFFER_SIZE, 0);
					else
						sound[i].extract(buffer[i], BUFFER_SIZE);
					normal[i] = false;
				break;
			}
			
			buffer[i].position = 0;
			
		  }

		  
		  
		  
		  for (var j:int = 0; j < BUFFER_SIZE*2; j++) 
		  {
			var val:Number = 0;
			
			for (var k:int = 0; k < 4; k++) 
			{
			  //var ampl:Number = ;
			  
			  switch(layers[k][loopCount])
			  {
					case 0:
						buffer[k].readFloat();
					break;
					case 4:
						val += buffer[k].readFloat() * volumes[k]*.25*XORandom();
					break;
					case 6:
						val += XORandom()*.1;
					break;
					case 8:
						val += buffer[k].readFloat() * volumes[k];
					break;
					case 12:
						val += buffer[k].readFloat() * volumes[k] * Number(interLoopCount/16);		
					break;
					default:  
						val += buffer[k].readFloat() * volumes[k]*.5;
					break;
			  }
			}
			
			//temp.y = 100*val;
			//FlxG.log(val);
			sampleDataEvent.data.writeFloat(val*currentPlaying);
			
			
		  }

		  interLoopCount++;
		  
		}
		
		private var ba:ByteArray = new ByteArray();
		private var sprites:Array = new Array();
		
		private var radio:Boolean = false;
		
		private function doFadeOut():void
		{
			for (var a:int = 0; a < 4; a++)
			{
				volumes[a] *= .98;
			}
		}
		
		private var last:int;
		
		override public function update():void
		{
			if (fadeOut)
			{
				doFadeOut();
			}
			
			//FlxG.log(soundc
			
			if (!radio)
			{
				super.update();
				super.collide();
			
				SoundMixer.computeSpectrum(ba);
			
			
				
				//FlxG.log(sndCChannel.position);
				
				/*
				for (var a:int = 0; a < 512; a++)
				{
					//FlxG.log(String(ba[a]));
				}*/
				
				var avg:Number = 0;
				
				if (playing)
				{
					for (var a:int = 0; a < 64; a++)
					{
						var spr:FlxSprite = sprites[a];
						spr.y = 16 - Number(ba[a * 4]) / 64;
						avg += Number(ba[a * 4]);

					}
				
				
					
					if (sndCChannel != null)
					{
						elapsed.x = (sndCChannel.position / 170666.666666) * 128 - 128;
						if (int(elapsed.x) != last)
						{
							last = int(elapsed.x);
							waveDraw.draw(new FlxSprite().createGraphic(1, (avg / 512), 0xff60c080), 127 + elapsed.x, 16 - (avg / 1024));
						}
					}
				
				}
				//FlxG.log(avg / 64);
				
			}
			
			//elapsed.x = -(32 - loopCount) * 4;
			
		}
		
		public var playing:Boolean = false;
		
			public function loadSample(evt:Event):void 
			{
				//Play loaded sound
				
				//sndChannel = soundClip.play();
				
				
				if (--loadCount <= 0)
				{
					playAll();
					loadingText.kill();
				}
				else
				{
					loadingText.text = String("Loading " + ((4-loadCount)*25) + "%..");
				}
			}
			
			private function getFlashVars():Object {
				return Object( LoaderInfo( this.loaderInfo ).parameters );
			}

			private var layer1:Array;
			private var layer2:Array;
			private var layer3:Array;
			private var layer4:Array;
			
			private var elapsed:FlxSprite;
			private var waveDraw:FlxSprite;
			
			private var start:Boolean = true;
			
			public function playAll():void
			{
				//add(new FlxText(0, 0, 100, "Hello, World!"));
				
				
				destroy();
				
				
				//if ((getFlashVars().progressionSample) == (getFlashVars().progression2Sample)  && (getFlashVars().drumsSample) == (getFlashVars().drums2Sample))
				//	elapsed.kill();
				
				if(getFlashVars().refresh!=null)
					refresh = Boolean(getFlashVars().refresh);
				else
					refresh = false;
				
				measureLength = nearestPowerOfTwo(int(5381.224489795918 / 168.16326530612244)) / 2;
				
				layer1 = new Array();
				layer2 = new Array();
				layer3 = new Array();
				layer4 = new Array();
				
				seed = int(getFlashVars().seed);
				granularity = int(getFlashVars().granularity);
				
				if (seed < 0)
				{
					seed = ((Number(getFlashVars().sample1volume) + String(getFlashVars().progressionSample).length + String(getFlashVars().progression2Sample).length + String(getFlashVars().drumsSample).length) % (-seed)) + 1;
				}
				
				if (granularity < 0)
				{
					granularity = ((Number(getFlashVars().sample3volume) + String(getFlashVars().progression2Sample).length + String(getFlashVars().drumsSample).length + String(getFlashVars().drums2Sample).length) % (-granularity))*2 + 2;
				}
				
				layer1.push(int(getFlashVars().one1));
				layer1.push(int(getFlashVars().one2));
				layer1.push(int(getFlashVars().one3));
				layer1.push(int(getFlashVars().one4));
				layer1.push(int(getFlashVars().one5));
				layer1.push(int(getFlashVars().one6));
				layer1.push(int(getFlashVars().one7));
				layer1.push(int(getFlashVars().one8));
				layer1.push(int(getFlashVars().one9));
				layer1.push(int(getFlashVars().one10));
				layer1.push(int(getFlashVars().one11));
				layer1.push(int(getFlashVars().one12));
				layer1.push(int(getFlashVars().one13));
				layer1.push(int(getFlashVars().one14));
				layer1.push(int(getFlashVars().one15));
				layer1.push(int(getFlashVars().one16));
				layer1.push(int(getFlashVars().one17));
				layer1.push(int(getFlashVars().one18));
				layer1.push(int(getFlashVars().one19));
				layer1.push(int(getFlashVars().one20));
				layer1.push(int(getFlashVars().one21));
				layer1.push(int(getFlashVars().one22));
				layer1.push(int(getFlashVars().one23));
				layer1.push(int(getFlashVars().one24));
				layer1.push(int(getFlashVars().one25));
				layer1.push(int(getFlashVars().one26));
				layer1.push(int(getFlashVars().one27));
				layer1.push(int(getFlashVars().one28));
				layer1.push(int(getFlashVars().one29));
				layer1.push(int(getFlashVars().one30));
				layer1.push(int(getFlashVars().one31));
				layer1.push(int(getFlashVars().one32));
				
				
				
				layer2.push(int(getFlashVars().two1));
				layer2.push(int(getFlashVars().two2));
				layer2.push(int(getFlashVars().two3));
				layer2.push(int(getFlashVars().two4));
				layer2.push(int(getFlashVars().two5));
				layer2.push(int(getFlashVars().two6));
				layer2.push(int(getFlashVars().two7));
				layer2.push(int(getFlashVars().two8));
				layer2.push(int(getFlashVars().two9));
				layer2.push(int(getFlashVars().two10));
				layer2.push(int(getFlashVars().two11));
				layer2.push(int(getFlashVars().two12));
				layer2.push(int(getFlashVars().two13));
				layer2.push(int(getFlashVars().two14));
				layer2.push(int(getFlashVars().two15));
				layer2.push(int(getFlashVars().two16));
				layer2.push(int(getFlashVars().two17));
				layer2.push(int(getFlashVars().two18));
				layer2.push(int(getFlashVars().two19));
				layer2.push(int(getFlashVars().two20));
				layer2.push(int(getFlashVars().two21));
				layer2.push(int(getFlashVars().two22));
				layer2.push(int(getFlashVars().two23));
				layer2.push(int(getFlashVars().two24));
				layer2.push(int(getFlashVars().two25));
				layer2.push(int(getFlashVars().two26));
				layer2.push(int(getFlashVars().two27));
				layer2.push(int(getFlashVars().two28));
				layer2.push(int(getFlashVars().two29));
				layer2.push(int(getFlashVars().two30));
				layer2.push(int(getFlashVars().two31));
				layer2.push(int(getFlashVars().two32));
				
				layer3.push(int(getFlashVars().three1));
				layer3.push(int(getFlashVars().three2));
				layer3.push(int(getFlashVars().three3));
				layer3.push(int(getFlashVars().three4));
				layer3.push(int(getFlashVars().three5));
				layer3.push(int(getFlashVars().three6));
				layer3.push(int(getFlashVars().three7));
				layer3.push(int(getFlashVars().three8));
				layer3.push(int(getFlashVars().three9));
				layer3.push(int(getFlashVars().three10));
				layer3.push(int(getFlashVars().three11));
				layer3.push(int(getFlashVars().three12));
				layer3.push(int(getFlashVars().three13));
				layer3.push(int(getFlashVars().three14));
				layer3.push(int(getFlashVars().three15));
				layer3.push(int(getFlashVars().three16));
				layer3.push(int(getFlashVars().three17));
				layer3.push(int(getFlashVars().three18));
				layer3.push(int(getFlashVars().three19));
				layer3.push(int(getFlashVars().three20));
				layer3.push(int(getFlashVars().three21));
				layer3.push(int(getFlashVars().three22));
				layer3.push(int(getFlashVars().three23));
				layer3.push(int(getFlashVars().three24));
				layer3.push(int(getFlashVars().three25));
				layer3.push(int(getFlashVars().three26));
				layer3.push(int(getFlashVars().three27));
				layer3.push(int(getFlashVars().three28));
				layer3.push(int(getFlashVars().three29));
				layer3.push(int(getFlashVars().three30));
				layer3.push(int(getFlashVars().three31));
				layer3.push(int(getFlashVars().three32));
				
				
				layer4.push(int(getFlashVars().four1));
				layer4.push(int(getFlashVars().four2));
				layer4.push(int(getFlashVars().four3));
				layer4.push(int(getFlashVars().four4));
				layer4.push(int(getFlashVars().four5));
				layer4.push(int(getFlashVars().four6));
				layer4.push(int(getFlashVars().four7));
				layer4.push(int(getFlashVars().four8));
				layer4.push(int(getFlashVars().four9));
				layer4.push(int(getFlashVars().four10));
				layer4.push(int(getFlashVars().four11));
				layer4.push(int(getFlashVars().four12));
				layer4.push(int(getFlashVars().four13));
				layer4.push(int(getFlashVars().four14));
				layer4.push(int(getFlashVars().four15));
				layer4.push(int(getFlashVars().four16));
				layer4.push(int(getFlashVars().four17));
				layer4.push(int(getFlashVars().four18));
				layer4.push(int(getFlashVars().four19));
				layer4.push(int(getFlashVars().four20));
				layer4.push(int(getFlashVars().four21));
				layer4.push(int(getFlashVars().four22));
				layer4.push(int(getFlashVars().four23));
				layer4.push(int(getFlashVars().four24));
				layer4.push(int(getFlashVars().four25));
				layer4.push(int(getFlashVars().four26));
				layer4.push(int(getFlashVars().four27));
				layer4.push(int(getFlashVars().four28));
				layer4.push(int(getFlashVars().four29));
				layer4.push(int(getFlashVars().four30));
				layer4.push(int(getFlashVars().four31));
				layer4.push(int(getFlashVars().four32));
				
				
				layers[0] = layer1;
				layers[1] = layer2;
				layers[2] = layer3;
				layers[3] = layer4;
				
				//destroy();
				//FlxG.flash = new FlxFlash();
				//FlxG.flash.start(0xffffffff, 1);
				
				/*
				var bg:FlxSprite = new FlxSprite(0, 0);
				bg.createGraphic(128, 32, 0xffffffff);
				bg.moves = false;
				add(bg);
				*/

				
				
				
				
				
				elapsed = new FlxSprite( -128, 0);
				elapsed.createGraphic(128, 32, 0xff777777);
				elapsed.fixed = true;
				elapsed.solid = false;
				add(elapsed);
				
				waveDraw = new FlxSprite(0, 0);
				waveDraw.createGraphic(128, 32, 0x00000000);
				waveDraw.fixed = true;
				waveDraw.solid = false;
				add(waveDraw);
				
				for (var a:int = 0; a < 64; a++)
				{
					sprites.push(new FlxSprite(a * 2, 0));
					FlxSprite(sprites[a]).createGraphic(2, 2, 0xffffffff);
					add(sprites[a]);
				}
								
				/*
				for (var a:int = 1; a < soundChannels.length; a++)
				{
					soundChannels[a] = Sound(sounds[a]).play();
				}*/
				
				sound[0] = sounds[0];
				sound[1] = sounds[1];
				sound[2] = sounds[2];
				sound[3] = sounds[3];
				
				
				trace(sound[0].length);
				trace(sound[1].length);
				trace(sound[2].length);
				trace(sound[3].length);
				
				/*
				var temp0:Number = nearestPowerOfTwo(int(sound[0].length / 168.16326530612244));
				var temp1:Number = nearestPowerOfTwo(int(sound[1].length / 168.16326530612244));
				var temp2:Number = nearestPowerOfTwo(int(sound[2].length / 168.16326530612244));
				var temp3:Number = nearestPowerOfTwo(int(sound[3].length / 168.16326530612244));
				
				if (lengths[0] < temp0)
				lengths[0] = nearestPowerOfTwo(int(sound[0].length / 168.16326530612244))/2;
				else
				lengths[0] = nearestPowerOfTwo(int(sound[0].length / 168.16326530612244));
				*/
				
				
				
				lengths[0] = (Math.floor(sound[0].length / 168.16326530612244)) + 2;
				lengths[0] -= lengths[0] % 32;
				lengths[1] = (Math.floor(sound[1].length / 168.16326530612244)) + 2;
				lengths[1] -= lengths[1] % 32;
				lengths[2] = (Math.floor(sound[2].length / 168.16326530612244)) + 2;
				lengths[2] -= lengths[2] % 32;
				lengths[3] = (Math.floor(sound[3].length / 168.16326530612244)) + 2;
				lengths[3] -= lengths[3] % 32;
				
				
				
				
				
				FlxG.log(lengths[0]);
				FlxG.log(lengths[1]);
				FlxG.log(lengths[2]);
				FlxG.log(lengths[3]);
				
				volumes[0] = Number(getFlashVars().sample1volume)/100;
				volumes[1] = Number(getFlashVars().sample2volume)/100;
				volumes[2] = Number(getFlashVars().sample3volume)/100;
				volumes[3] = Number(getFlashVars().sample4volume)/100;
				
				buffer[0] = new ByteArray();
				buffer[1] = new ByteArray();
				buffer[2] = new ByteArray();
				buffer[3] = new ByteArray();
				
				soundC = new Sound();
				soundC.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
				
				sndCChannel = new SoundChannel();
				
				
				if(getFlashVars().radio != null)
					radio = true;
				
				
				if (!radio || String(getFlashVars().radio) == "start")
				{
					playing = true;
					sndCChannel = soundC.play();
					
				}
				
				
				FlxG.mouse.hide();
				//FlxG.mouse.cursor.x = -100;
				//FlxG.mouse.cursor.y = -100;
				
			}
			
			private var soundC:Sound;
			
			public function startPlay():void
			{
				playing = true;
				sndCChannel = soundC.play();
			}
			
			public function changeVolume(val:Number):void
			{
				//add(new FlxText(0, 0, 100, "Hello, World!"));
				SoundMixer.soundTransform = new SoundTransform(val / 100);
			}
		
			private var loadingText:FlxText;
			
		override public function create():void
		{
			//bgColor = 0xffffffff;
			ExternalInterface.addCallback("changeVolume", changeVolume);
			ExternalInterface.addCallback("startPlay", startPlay);
			
			//add(new FlxText(0, 0, 100, "Hello, World!")); //adds a 100px wide text field at position 0,0 (top left)
			/*
			var bg:FlxSprite = new FlxSprite(0, 0);
			bg.createGraphic(128, 32, 0xffffffff);
			bg.moves = false;
			add(bg);
			*/
			loadingText = new FlxText(0, 0, 100, String("Loading 0%.."));
			add(loadingText);
			super.update();
			
			
			
			sounds = new Array();
			soundChannels = new Array();
			
			loadCount = 5;
			
			
			SoundMixer.soundTransform = new SoundTransform(getFlashVars().mastervolume / 100);
			
			/////////////////////
			
			soundClip = new Sound();
			sndChannel = new SoundChannel();
			
			//soundClip.load(new URLRequest("http://extlabs.com/canopy/sites/default/files/allen1_0.mp3"));
			soundClip.load(new URLRequest(String(getFlashVars().progressionSample)));
			soundClip.addEventListener(Event.COMPLETE,loadSample,false,0,true);
			
			sounds.push(soundClip);
			soundChannels.push(sndChannel);
			
			soundClip = new Sound();
			sndChannel = new SoundChannel();
			
			//soundClip.load(new URLRequest("http://extlabs.com/canopy/sites/default/files/allen1_0.mp3"));
			soundClip.load(new URLRequest(String(getFlashVars().progression2Sample)));
			soundClip.addEventListener(Event.COMPLETE,loadSample,false,0,true);
			
			sounds.push(soundClip);
			soundChannels.push(sndChannel);
			
			soundClip = new Sound();
			sndChannel = new SoundChannel();
			
			//soundClip.load(new URLRequest("http://extlabs.com/canopy/sites/default/files/allen1_0.mp3"));
			soundClip.load(new URLRequest(String(getFlashVars().drumsSample)));
			soundClip.addEventListener(Event.COMPLETE,loadSample,false,0,true);
			
			sounds.push(soundClip);
			soundChannels.push(sndChannel);
			
			soundClip = new Sound();
			sndChannel = new SoundChannel();
			
			//soundClip.load(new URLRequest("http://extlabs.com/canopy/sites/default/files/allen1_0.mp3"));
			soundClip.load(new URLRequest(String(getFlashVars().drums2Sample)));
			soundClip.addEventListener(Event.COMPLETE,loadSample,false,0,true);
			
			sounds.push(soundClip);
			soundChannels.push(sndChannel);
			
			
			loadCount--;
			
			
			
			
		}
	}
}